<template>
  <div class="bpmn-container">
    <div class="toolbar">
      <el-button-group>
        <el-button type="primary" @click="saveDiagram">保存</el-button>
        <el-button @click="exportDiagram">导出</el-button>
        <el-button @click="importDiagram">导入</el-button>
      </el-button-group>
    </div>
    <div class="canvas-container">
      <div class="properties-panel-parent" ref="propertiesPanel"></div>
      <div class="canvas" ref="canvas"></div>
    </div>
    <input type="file" ref="fileInput" style="display: none" @change="handleFileSelect" accept=".bpmn, .xml" />
  </div>
</template>

<script>
import BpmnModeler from 'bpmn-js/lib/Modeler';
import 'bpmn-js/dist/assets/diagram-js.css';
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css';
import camundaModdleDescriptor from 'camunda-bpmn-moddle/resources/camunda.json';
import { ref, onMounted } from 'vue';

export default {
  name: 'BpmnEditor',
  props: {
    initialXml: {
      type: String,
      default: null
    }
  },
  emits: ['save', 'error'],
  setup(props, { emit }) {
    const canvas = ref(null);
    const propertiesPanel = ref(null);
    const fileInput = ref(null);
    let modeler = null;

    const defaultXml = `<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn">
  <bpmn:process id="Process_1" name="流程" isExecutable="true">
    <bpmn:startEvent id="StartEvent_1" name="开始">
      <bpmn:outgoing>Flow_1</bpmn:outgoing>
    </bpmn:startEvent>
    <bpmn:endEvent id="EndEvent_1" name="结束">
      <bpmn:incoming>Flow_1</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:sequenceFlow id="Flow_1" sourceRef="StartEvent_1" targetRef="EndEvent_1" />
  </bpmn:process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
      <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
        <dc:Bounds x="179" y="159" width="36" height="36" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="186" y="202" width="22" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="EndEvent_1_di" bpmnElement="EndEvent_1">
        <dc:Bounds x="592" y="159" width="36" height="36" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="599" y="202" width="22" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="Flow_1_di" bpmnElement="Flow_1">
        <di:waypoint x="215" y="177" />
        <di:waypoint x="592" y="177" />
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn:definitions>`;

    const createNewDiagram = (xml) => {
      // 创建BPMN建模器
      modeler = new BpmnModeler({
        container: canvas.value,
        propertiesPanel: {
          parent: propertiesPanel.value
        },
        additionalModules: [],
        moddleExtensions: {
          camunda: camundaModdleDescriptor
        }
      });

      // 导入BPMN图
      modeler
        .importXML(xml || defaultXml)
        .then(({ warnings }) => {
          if (warnings.length) {
            console.warn('BPMN导入警告:', warnings);
          }
          // 调整画布大小
          modeler.get('canvas').zoom('fit-viewport');
        })
        .catch((err) => {
          console.error('BPMN导入错误:', err);
          emit('error', '导入BPMN图失败: ' + err.message);
        });
    };

    const saveDiagram = () => {
      if (!modeler) return;

      modeler
        .saveXML({ format: true })
        .then(({ xml }) => {
          emit('save', xml);
        })
        .catch((err) => {
          console.error('保存BPMN图失败:', err);
          emit('error', '保存BPMN图失败: ' + err.message);
        });
    };

    const exportDiagram = () => {
      if (!modeler) return;

      modeler
        .saveXML({ format: true })
        .then(({ xml }) => {
          const blob = new Blob([xml], { type: 'application/xml' });
          const link = document.createElement('a');
          link.href = URL.createObjectURL(blob);
          link.download = 'diagram.bpmn';
          document.body.appendChild(link);
          link.click();
          document.body.removeChild(link);
        })
        .catch((err) => {
          console.error('导出BPMN图失败:', err);
          emit('error', '导出BPMN图失败: ' + err.message);
        });
    };

    const importDiagram = () => {
      if (!fileInput.value) return;
      fileInput.value.click();
    };

    const handleFileSelect = (event) => {
      const file = event.target.files[0];
      if (!file) return;

      const reader = new FileReader();
      reader.onload = (e) => {
        const xml = e.target.result;
        modeler
          .importXML(xml)
          .then(({ warnings }) => {
            if (warnings.length) {
              console.warn('BPMN导入警告:', warnings);
            }
            // 调整画布大小
            modeler.get('canvas').zoom('fit-viewport');
          })
          .catch((err) => {
            console.error('导入BPMN图失败:', err);
            emit('error', '导入BPMN图失败: ' + err.message);
          });
      };
      reader.readAsText(file);
      event.target.value = null; // 重置input以便再次选择同一文件
    };

    onMounted(() => {
      createNewDiagram(props.initialXml);
    });

    return {
      canvas,
      propertiesPanel,
      fileInput,
      saveDiagram,
      exportDiagram,
      importDiagram,
      handleFileSelect
    };
  }
};
</script>

<style scoped>
.bpmn-container {
  height: 100%;
  width: 100%;
  display: flex;
  flex-direction: column;
}

.toolbar {
  padding: 10px;
  background-color: #f5f7fa;
  border-bottom: 1px solid #dcdfe6;
}

.canvas-container {
  flex: 1;
  display: flex;
  overflow: hidden;
}

.properties-panel-parent {
  width: 300px;
  overflow: auto;
  border-right: 1px solid #dcdfe6;
}

.canvas {
  flex: 1;
  height: 100%;
}

:deep(.djs-palette) {
  border-right: 1px solid #dcdfe6;
}
</style> 